/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.iec.edp.caf.rpc.api.utils;

import io.iec.edp.caf.rpc.api.annotation.GspReturnSerializeType;
import io.iec.edp.caf.rpc.api.annotation.GspServiceBundle;
import io.iec.edp.caf.rpc.api.annotation.RpcParam;
import io.iec.edp.caf.rpc.api.annotation.RpcServiceMethod;
import io.iec.edp.caf.rpc.api.common.GspSerializeType;
import io.iec.edp.caf.rpc.api.entity.RpcParamDefinition;
import io.iec.edp.caf.rpc.api.entity.RpcReturnValueDefinition;
import io.iec.edp.caf.rpc.api.entity.RpcServiceDefinition;
import io.iec.edp.caf.rpc.api.entity.RpcServiceMethodDefinition;
import io.iec.edp.caf.rpc.api.schema.*;
import io.iec.edp.caf.rpc.api.schema.enums.TypeEnum;
import org.apache.commons.lang3.StringUtils;

import java.lang.reflect.*;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.*;

/**
 * @author Leon Huo
 */
public class DefinitionUtil {
    /**
     * 转换serviceDefinition
     * <p>
     * //todo泛型方法暂不支持
     *
     * @param c
     * @return
     */
    public static RpcServiceDefinition transformServiceDefinition(Class<?> c) {
        RpcServiceDefinition rpcServiceDefinition = new RpcServiceDefinition();
        GspServiceBundle gspServiceBundle = c.getDeclaredAnnotation(GspServiceBundle.class);

        //主键为包名+类名+版本号
        rpcServiceDefinition.setId(c.getName() + ":" + gspServiceBundle.serviceVersion());
        //Name先从注解中取 没有则为包名+类名
        rpcServiceDefinition.setName(gspServiceBundle.serviceName() == "" ? c.getName() : gspServiceBundle.serviceName());
        //真实的类名 保证可以通过反射拿到interfaceClass
        rpcServiceDefinition.setClassName(c.getName());
        //todo 描述目前无法定义
        rpcServiceDefinition.setDescription("");
        //版本号
        rpcServiceDefinition.setVersion(gspServiceBundle.serviceVersion());
        //关键应用
        rpcServiceDefinition.setApp(gspServiceBundle.applicationName());
        //服务单元
        rpcServiceDefinition.setSu(gspServiceBundle.serviceUnitName());

        //组装方法信息
        Method[] iMethods = c.getMethods();
        List<RpcServiceMethodDefinition> methodDefinitions = new ArrayList<>();
        for (Method m : iMethods) {
            RpcServiceMethodDefinition methodDefinition = transformMethodDefinition(c, m, rpcServiceDefinition);
            methodDefinition.setParentDefinition(rpcServiceDefinition);
            methodDefinitions.add(methodDefinition);
        }
        rpcServiceDefinition.setMethods(methodDefinitions);

        return rpcServiceDefinition;
    }

    /**
     * 转换serviceMethodDefinition
     *
     * @param interfaceClass
     * @param method
     * @return
     */
    public static RpcServiceMethodDefinition transformMethodDefinition(Class<?> interfaceClass, Method method, RpcServiceDefinition rpcServiceDefinition) {
        RpcServiceMethodDefinition methodDefinition = new RpcServiceMethodDefinition();

//        //与父级的关联
//        methodDefinition.setServiceDefinitionId(rpcServiceDefinition.getId());
        //id默认为包名+类名+version+方法名
        methodDefinition.setId(rpcServiceDefinition.getId() + "." + method.getName());

        //设置serviceId 默认为包名+类名+方法名 可自定义
        RpcServiceMethod annotation = method.getAnnotation(RpcServiceMethod.class);
        String serviceId="";
        if (annotation != null) {
            serviceId = annotation.serviceId();
        }

        if ("".equals(serviceId))
            serviceId = interfaceClass.getName() + "." + method.getName();
        //serviceId = annotation == null ? interfaceClass.getName() + "." + method.getName() : annotation.serviceId();
        methodDefinition.setServiceId(serviceId);

        //todo 暂时没有定义 默认取这个
        methodDefinition.setName(interfaceClass.getName() + "." + method.getName());
        methodDefinition.setDescription("");

        //设置返回值
        RpcReturnValueDefinition returnValueDefinition = transformReturnValueDefinition(method);
        methodDefinition.setReturnInfo(returnValueDefinition);

        //设置参数
        List<RpcParamDefinition> paramDefinitions = new ArrayList<>();
        for (Parameter param : method.getParameters()) {
            RpcParamDefinition paramDefinition = transformParamDefinition(param);
            paramDefinitions.add(paramDefinition);
        }

        methodDefinition.setParameters(paramDefinitions);
        return methodDefinition;
    }

    /**
     * 转换Rpc方法参数定义
     *
     * @param param
     * @return
     */
    public static RpcParamDefinition transformParamDefinition(Parameter param) {

        RpcParamDefinition rpcParamDefinition = new RpcParamDefinition();
        RpcParam rpcParam = param.getDeclaredAnnotation(RpcParam.class);
        if (rpcParam == null) {
            rpcParamDefinition.setName(param.getName());
            rpcParamDefinition.setSerializeType(GspSerializeType.Json);
            rpcParamDefinition.setSerializer("");
        } else {
            rpcParamDefinition.setName(rpcParam.paramName());
            rpcParamDefinition.setSerializer(rpcParam.customSerializeTypeRef());
            rpcParamDefinition.setSerializeType(rpcParam.paramSerializeType());
        }

        //todo 以下属性暂时没有定义 扩展用
        rpcParamDefinition.setAlias("");
        rpcParamDefinition.setDescription("");
        rpcParamDefinition.setDefaultValue(null);
        rpcParamDefinition.setRequired(false);


        rpcParamDefinition.setType(transformTypeSchema(param.getParameterizedType(), new HashMap<>()));
        return rpcParamDefinition;
    }

    /**
     * 转换returnValueDefinition
     *
     * @param method
     * @return
     */
    public static RpcReturnValueDefinition transformReturnValueDefinition(Method method) {
        RpcReturnValueDefinition rpcReturnValueDefinition = new RpcReturnValueDefinition();
        GspReturnSerializeType returnAnnotation = method.getDeclaredAnnotation(GspReturnSerializeType.class);
        //因存在循环引用，临时屏蔽类型转换
        rpcReturnValueDefinition.setType(transformTypeSchema(method.getGenericReturnType(), new HashMap<>()));

        if (returnAnnotation == null) {
            rpcReturnValueDefinition.setSerializer("");
            rpcReturnValueDefinition.setSerializeType(GspSerializeType.Json);
        } else {
            rpcReturnValueDefinition.setSerializer(returnAnnotation.customSerializeTypeRef());
            rpcReturnValueDefinition.setSerializeType(returnAnnotation.paramSerializeType());
        }

        return rpcReturnValueDefinition;
    }

    /**
     * 将Class转换成schema
     *
     * @param type
     * @return
     */
    public static TypeSchema transformTypeSchema(Type type, HashMap<String, TypeSchema> repeatMap) {
        if (type instanceof Class) {
            //无泛型类型
            //基本数据类型
            if (type == Byte.class || type == byte.class) {
                return TypeSchemaConstant.BYTE;
            } else if (type == Short.class || type == short.class) {
                return TypeSchemaConstant.SHORT;
            } else if (type == Integer.class || type == int.class) {
                return TypeSchemaConstant.INT;
            } else if (type == Long.class || type == long.class) {
                return TypeSchemaConstant.LONG;
            } else if (type == Float.class || type == float.class) {
                return TypeSchemaConstant.FLOAT;
            } else if (type == Double.class || type == double.class) {
                return TypeSchemaConstant.DOUBLE;
            } else if (type == Character.class || type == char.class) {
                return TypeSchemaConstant.CHAR;
            } else if (type == Boolean.class || type == boolean.class) {
                return TypeSchemaConstant.BOOL;
            } else if (type == String.class) {
                return TypeSchemaConstant.STRING;
            } else if (type == BigDecimal.class) {
                return TypeSchemaConstant.BIG_DECIMAL;
            } else if (type == BigInteger.class) {
                return TypeSchemaConstant.BIG_INTEGER;
            } else if (type == Date.class) {
                return TypeSchemaConstant.DATE;
            } else if (type == Object.class) {
                return TypeSchemaConstant.UNKNOWN;
            } else if (type == void.class) {
                return TypeSchemaConstant.VOID;
            } else if (type == TypeSchema.class) {
                return TypeSchemaConstant.TYPE_SCHEMA;
            }else if (type == MapSchema.class) {
                return TypeSchemaConstant.MAP_SCHEMA;
            }else if (type == ArraySchema.class) {
                return TypeSchemaConstant.ARRAY_SCHEMA;
            }else if (type == ObjectSchema.class) {
                return TypeSchemaConstant.OBJECT_SCHEMA;
            }else if (type == SchemaField.class) {
                return TypeSchemaConstant.SCHEMA_FIELD;
            }else if (type == TypeEnum.class) {
                return TypeSchemaConstant.TYPE_ENUM;
            }else if (Collection.class.isAssignableFrom((Class) type)) {
                //无泛型list 可以认为是List<Object>
                return new ArraySchema(TypeSchemaConstant.UNKNOWN);
            } else if (((Class) type).isArray()) {
                //数组类型
                return new ArraySchema(transformTypeSchema(((Class) type).getComponentType(), repeatMap));
            } else if (Map.class.isAssignableFrom((Class) type)) {
                //无泛型Map
                return new MapSchema(TypeSchemaConstant.UNKNOWN, TypeSchemaConstant.UNKNOWN);
            } else if (((Class) type).isInterface()) {
                //接口类型
                return new TypeSchema(type.getTypeName(), TypeEnum.INTERFACE);
            } else if (((Class) type).isEnum()) {
                //枚举类型
                return new TypeSchema(type.getTypeName(), TypeEnum.ENUM);
            } else {
                //普通Object
                //循环引用直接return
                if (repeatMap.get(type.getTypeName()) != null) {
                    return repeatMap.get(type.getTypeName());
                }

                //非循环引用 创建变量并放进map
                ObjectSchema schema = new ObjectSchema();
                schema.setClassName(type.getTypeName());
                //设置参数类型，临时屏蔽，此处造成循环引用

//                repeatMap.put(type.getTypeName(), schema);
//
//                //循环扫描字段
//                List<SchemaField> fieldList = new ArrayList<>();
//                for (Method method : ((Class) type).getMethods()) {
//                    SchemaField schemaField = getWrapMethodField(((Class) type), method, repeatMap);
//                    if (schemaField != null) {
//                        fieldList.add(schemaField);
//                    }
//                }
//
//                schema.setSubFieldList(fieldList.toArray(new SchemaField[0]));

                return schema;
            }
        } else if (type instanceof ParameterizedType) {
            //带泛型类型
            Class<?> rawType = (Class<?>) ((ParameterizedType) type).getRawType();
            Type[] actualArguments = ((ParameterizedType) type).getActualTypeArguments();
            if (Collection.class.isAssignableFrom(rawType)) {
                //泛型list 可以认为是List<Object>
                return new ArraySchema(transformTypeSchema(actualArguments[0], repeatMap));
            } else if (Map.class.isAssignableFrom(rawType)) {
                //泛型Map
                return new MapSchema(transformTypeSchema(actualArguments[0], repeatMap), transformTypeSchema(actualArguments[1], repeatMap));
            } else if (rawType.isInterface()) {
                //接口类型
                return new TypeSchema(type.getTypeName(), TypeEnum.INTERFACE);
            } else {
                //普通Object
                //循环引用直接return
                if (repeatMap.get(type.getTypeName()) != null) {
                    return repeatMap.get(type.getTypeName());
                }

                //非循环引用 创建变量并放进map
                ObjectSchema schema = new ObjectSchema();
                schema.setClassName(type.getTypeName());
                repeatMap.put(type.getTypeName(), schema);
//设置参数类型，临时屏蔽，此处造成循环引用

//                //建立泛型和真实类型的映射
//                Map<String, TypeSchema> genericMap = new HashMap<>();
//                Type[] genericType = rawType.getTypeParameters();
//
//                //循环设置泛型
//                TypeSchema[] generics = new TypeSchema[actualArguments.length];
//                for (int i = 0; i < actualArguments.length; i++) {
//                    generics[i] = transformTypeSchema(actualArguments[i], repeatMap);
//                    genericMap.put(((TypeVariable) genericType[i]).getName(), generics[i]);
//                }
//                schema.setGenericClass(generics);
//
//                //循环扫描字段
//                List<SchemaField> fieldList = new ArrayList<>();
//                for (Method method : rawType.getMethods()) {
//                    SchemaField schemaField = getWrapMethodField(rawType, method, repeatMap);
//                    if (schemaField != null) {
//                        //如果是泛型类型 则
//                        //todo 目前只能处理一层嵌套
//                        if (genericMap.get(schemaField.getType().getClassName()) != null) {
//                            SchemaField realSchemaField = new SchemaField(schemaField.getName());
//                            realSchemaField.setType(genericMap.get(schemaField.getType().getClassName()));
//                            fieldList.add(realSchemaField);
//                        } else {
//                            fieldList.add(schemaField);
//                        }
//                    }
//                }
//
//                schema.setSubFieldList(fieldList.toArray(new SchemaField[0]));

                return schema;
            }
        } else if (type instanceof GenericArrayType) {
            //带泛型的数组类型
            return new ArraySchema(transformTypeSchema(((GenericArrayType) type).getGenericComponentType(), repeatMap));
        }

        return new TypeSchema(type.getTypeName(), TypeEnum.UNKNOWN);
    }

    //获取封装方法(get/set方法的返回字段)
    private static SchemaField getWrapMethodField(Class<?> rawType, Method getMethod,
                                                  HashMap<String, TypeSchema> repeatMap) {
        if (getMethod.getName().startsWith("get")) {
            //解析字段名称
            String fieldName = toSmallCamel(getMethod.getName().replaceFirst("get", ""));
            if (fieldName == null) {
                return null;
            }

            Method setMethod;
            //判断set方法是否存在 字段是否存在
            try {
                setMethod = rawType.getMethod(getMethod.getName().replaceFirst("get", "set"), getMethod.getReturnType());
                rawType.getDeclaredField(fieldName);
            } catch (NoSuchMethodException | NoSuchFieldException e) {
                return null;
            }

            //判断set方法参数是否唯一 是否类型和get方法返回值相同
            if (!getMethod.getGenericReturnType().equals(setMethod.getParameters()[0].getParameterizedType())) {
                return null;
            }

            SchemaField schemaField = new SchemaField(fieldName);
            schemaField.setType(transformTypeSchema(getMethod.getGenericReturnType(), repeatMap));

            return schemaField;
        }

        return null;
    }

    //将BigCamel改成SmallCamel
    private static String toSmallCamel(String s) {
        if (!StringUtils.isEmpty(s) && s.charAt(0) >= 'A' && s.charAt(0) <= 'Z') {
            StringBuilder stringBuilder = new StringBuilder(s);
            stringBuilder.setCharAt(0, (char) ((int) s.charAt(0) + 32));
            return stringBuilder.toString();
        }

        return null;
    }
}
